//
//  pcap_engine_libpcap.cpp
//  testpcap
//
//  Created by bugforever on 1/10/15.
//  Copyright (c) 2015 bugforever. All rights reserved.
//

#define _XKEYCHECK_H

#include "pcap_engine.h"
#include <iostream>
#include "../../Include/network_struct.h"
#include "assert.h"
#include "../utility/network_data.h"


typedef struct ether_header {
    u_char ether_shost[ETHER_ADDR_LEN]; /* source ethernet address, 8 bytes */
    u_char ether_dhost[ETHER_ADDR_LEN]; /* destination ethernet addresss, 8 bytes */
    u_short ether_type;                 /* ethernet type, 16 bytes */
}ether_header;

pcap_engine::pcap_engine()
: network_filter_base( FAD_ETHERNET , FAD_ETHERNET )
, m_handle_pcap(0)
, m_link_type(0)
{
    ;
}

pcap_engine::~pcap_engine()
{
    ;
}

bool pcap_engine::start_engine( device_info * ptr_info )
{
    m_capture_device = *ptr_info;
    
    int ret = start_thread();
    
    if( ret == -1 )
        return false;
    
    return true;
}

bool pcap_engine::stop_engine()
{
    pcap_breakloop( m_handle_pcap );
    
    stop_thread();
    
    pcap_close( m_handle_pcap );
    m_handle_pcap = 0;
    
    return true;
}

void pcap_engine::thread_function()
{
    char errbuf[PCAP_ERRBUF_SIZE];
    m_handle_pcap = pcap_create(m_capture_device.get_name(), errbuf);
    
    if( !m_handle_pcap )
        return;
    
    int ret = pcap_set_snaplen(m_handle_pcap, 65535);
    
    if( ret != 0 )
        return;
    
#ifndef _WIN32
    ret = pcap_set_immediate_mode(m_handle_pcap, 1);
#endif
    
    if( ret != 0 )
        return;
    
    ret = pcap_set_timeout(m_handle_pcap, 1000);
    
    if( ret != 0 )
        return;
    
    ret = pcap_activate( m_handle_pcap );
    
    if( ret != 0 )
        return;
    
    m_link_type = pcap_datalink( m_handle_pcap );
    
    if( m_link_type != DLT_EN10MB )
    {
        std::cout<<"We do not support kind link type right now: "<<m_link_type<<std::endl;
        return;
    }
    
    ret = pcap_loop(m_handle_pcap, 0, pcap_handler, (u_char *)this );
}

void pcap_engine::process_network_data( FILTER_ACCEPT_DATATYPE type , void * ptr_data )
{
    assert( FAD_PCAP == type );
    
    network_pcap_data * ptr_pcap_data = ( network_pcap_data * )ptr_data;
    ether_header * ptr_ethernet_header = (ether_header *)ptr_pcap_data->ptr_data->get_raw_data();
    
    network_ethernet_data ethernet_data;
    
    ethernet_data.time = ptr_pcap_data->time;
    memcpy( ethernet_data.ether_shost , ptr_ethernet_header->ether_shost , ETHER_ADDR_LEN );
    memcpy( ethernet_data.ether_dhost , ptr_ethernet_header->ether_dhost, ETHER_ADDR_LEN );
    ethernet_data.ether_type = ntohs(ptr_ethernet_header->ether_type);
    
    ptr_pcap_data->ptr_data->set_tag( NDT_ETHERNET_DATA , 14 );

	ethernet_data.ptr_data = ptr_pcap_data->ptr_data;
    
    send_network_data( FAD_ETHERNET , &ethernet_data );
}

void pcap_engine::pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
    pcap_engine * ptr_obj = ( pcap_engine * )user;
    
    network_pcap_data pcap_data;
    
    pcap_data.time = h->ts.tv_sec;
    
    pcap_data.ptr_data = new network_data();
    pcap_data.ptr_data->append_data( ( unsigned char * )bytes , h->caplen );
    pcap_data.ptr_data->add_ref();
    
    ptr_obj->process_network_data(FAD_PCAP, &pcap_data);

    pcap_data.ptr_data->release();
}








